aboutsummaryrefslogtreecommitdiff
path: root/src/app/(main)/websites/[websiteId]/sessions/SessionActivity.tsx
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-01-24 13:09:50 +0000
committerFuwn <[email protected]>2026-01-24 13:09:50 +0000
commit396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b (patch)
treeb9df4ca6a70db45cfffbae6fdd7252e20fb8e93c /src/app/(main)/websites/[websiteId]/sessions/SessionActivity.tsx
downloadumami-main.tar.xz
umami-main.zip
Initial commitHEADmain
Created from https://vercel.com/new
Diffstat (limited to 'src/app/(main)/websites/[websiteId]/sessions/SessionActivity.tsx')
-rw-r--r--src/app/(main)/websites/[websiteId]/sessions/SessionActivity.tsx94
1 files changed, 94 insertions, 0 deletions
diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionActivity.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionActivity.tsx
new file mode 100644
index 0000000..cbb2810
--- /dev/null
+++ b/src/app/(main)/websites/[websiteId]/sessions/SessionActivity.tsx
@@ -0,0 +1,94 @@
+import {
+ Button,
+ Column,
+ Dialog,
+ DialogTrigger,
+ Heading,
+ Icon,
+ Popover,
+ Row,
+ StatusLight,
+ Text,
+} from '@umami/react-zen';
+import { isSameDay } from 'date-fns';
+import { LoadingPanel } from '@/components/common/LoadingPanel';
+import { useMessages, useMobile, useSessionActivityQuery, useTimezone } from '@/components/hooks';
+import { Eye, FileText } from '@/components/icons';
+import { EventData } from '@/components/metrics/EventData';
+import { Lightning } from '@/components/svg';
+
+export function SessionActivity({
+ websiteId,
+ sessionId,
+ startDate,
+ endDate,
+}: {
+ websiteId: string;
+ sessionId: string;
+ startDate: Date;
+ endDate: Date;
+}) {
+ const { formatMessage, labels } = useMessages();
+ const { formatTimezoneDate } = useTimezone();
+ const { data, isLoading, error } = useSessionActivityQuery(
+ websiteId,
+ sessionId,
+ startDate,
+ endDate,
+ );
+ const { isMobile } = useMobile();
+ let lastDay = null;
+
+ return (
+ <LoadingPanel data={data} isLoading={isLoading} error={error}>
+ <Column gap>
+ {data?.map(({ eventId, createdAt, urlPath, eventName, visitId, hasData }) => {
+ const showHeader = !lastDay || !isSameDay(new Date(lastDay), new Date(createdAt));
+ lastDay = createdAt;
+
+ return (
+ <Column key={eventId} gap>
+ {showHeader && <Heading size="1">{formatTimezoneDate(createdAt, 'PPPP')}</Heading>}
+ <Row alignItems="center" gap="6" height="40px">
+ <StatusLight color={`#${visitId?.substring(0, 6)}`}>
+ <Text wrap="nowrap">{formatTimezoneDate(createdAt, 'pp')}</Text>
+ </StatusLight>
+ <Row alignItems="center" gap="2">
+ <Icon>{eventName ? <Lightning /> : <Eye />}</Icon>
+ <Text wrap="nowrap">
+ {eventName
+ ? formatMessage(labels.triggeredEvent)
+ : formatMessage(labels.viewedPage)}
+ </Text>
+ <Text weight="bold" style={{ maxWidth: isMobile ? '400px' : null }} truncate>
+ {eventName || urlPath}
+ </Text>
+ {hasData > 0 && <PropertiesButton websiteId={websiteId} eventId={eventId} />}
+ </Row>
+ </Row>
+ </Column>
+ );
+ })}
+ </Column>
+ </LoadingPanel>
+ );
+}
+
+const PropertiesButton = props => {
+ return (
+ <DialogTrigger>
+ <Button variant="quiet">
+ <Row alignItems="center" gap>
+ <Icon>
+ <FileText />
+ </Icon>
+ </Row>
+ </Button>
+ <Popover placement="right">
+ <Dialog>
+ <EventData {...props} />
+ </Dialog>
+ </Popover>
+ </DialogTrigger>
+ );
+};